package scli

import (
	"github.com/urfave/cli/v2"
)

type (
	Options struct {
		// flags will set value which following --xxx to parameter
		flags []cli.Flag
		// commands will be trigger by flag xxx
		commands []*cli.Command
	}

	BaseCfg struct {
		//Name is the command full name
		Name string
		//Short is the command aliases name
		Short string
		//Usage is the description of command
		Usage string
		//Required means this flag must be set
		Required bool
	}
	//FlagCfg is the config of parameter
	FlagCfg struct {
		*BaseCfg
		//Default value
		Default string
		//Dest is pointer of value
		Dest interface{}
	}

	//CommandCfg is the config of command
	CommandCfg struct {
		*BaseCfg
		Action func([]string) error
	}

	Option func(options *Options)
)

//WithStringFlag will create string flag
func WithStringFlag(flag *FlagCfg) Option {

	if flag == nil {
		panic("invalid flag config ")
	}

	if len(flag.Name) == 0 && len(flag.Short) == 0 {
		panic("flag's name or short name must set value")
	}

	return func(o *Options) {
		sf := &cli.StringFlag{
			Name:        flag.Name,
			Aliases:     []string{flag.Short},
			Value:       flag.Default,
			Usage:       flag.Usage,
			Required:    flag.Required,
			Destination: flag.Dest.(*string),
		}
		o.flags = append(o.flags, sf)
	}
}

func WithFlag(flag *FlagCfg) Option {
	var opt Option
	switch dst := flag.Dest.(type) {
	case *string:
		opt = func(o *Options) {
			sf := &cli.StringFlag{
				Name:        flag.Name,
				Aliases:     []string{flag.Short},
				Value:       flag.Default,
				Usage:       flag.Usage,
				Required:    flag.Required,
				Destination: dst,
			}
			o.flags = append(o.flags, sf)
		}
	case *bool:
		opt = func(o *Options) {
			sf := &cli.BoolFlag{
				Name:        flag.Name,
				Aliases:     []string{flag.Short},
				Value:       false,
				Usage:       flag.Usage,
				Required:    flag.Required,
				Destination: dst,
			}
			o.flags = append(o.flags, sf)
		}
	case cli.Generic:
		//增加复合类型
		opt = func(o *Options) {
			sf := &cli.GenericFlag{
				Name:     flag.Name,
				Aliases:  []string{flag.Short},
				Usage:    flag.Usage,
				Required: flag.Required,
				Value:    dst,
			}
			o.flags = append(o.flags, sf)
		}

	default:
		panic("unsupported flag type !")
	}
	return opt
}

func WithCommand(command *CommandCfg) Option {
	if command == nil {
		panic("invalid command config ")
	}

	if len(command.Name) == 0 && len(command.Short) == 0 {
		panic("command's name or short name must set value")
	}
	return func(o *Options) {
		cmd := &cli.Command{
			Name:    command.Name,
			Usage:   command.Usage,
			Aliases: []string{command.Short},
		}
		act := command.Action
		if act != nil {
			cmd.Action = func(ctx *cli.Context) error {
				return act(ctx.Args().Slice())
			}
		}
		o.commands = append(o.commands, cmd)
	}
}
